#include <memory>

#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "Daemon.hpp"

using namespace my_tcpserver;
using namespace my_protocol;

// 【服务器注意一】 1. 当读取信息失败, 要break;  2. 设置 signal(SIGPIPE, SIG_IGN);
// 解释: recv相当于read  send相当于write
// 当客户端关闭, 写端关闭，服务器recv读取不了消息了, 返回0, break结束服务此客户端的线程
// 当服务器send 正在发消息, 客户端关闭，客户端recv读端关闭, 那么服务器的send操作异常(非法写入),会发送SIGPIPE信号导致服务器退出， 所以需要signal(SIGPIPE, SIG_IGN), 忽略

// 【服务器注意二】
// UDP是面向数据报: 客户端每次发都是发一个完整的报文，给服务器，服务器每次recv读的都是一个完整的报文
// TCP 面向字节流的: 
// 1. 客户端send时，不是直接发给服务器，而是将消息放在 【发送缓冲区】
// 2. 服务器recv时，是从【接受缓冲区】里面拿数据的
// 服务器recv从缓冲区拿数据时，可能拿到一个不完整的报文，也可能拿到的是几个报文，也可能是几个加一个不完整报文
// 所以recv需要特殊处理，保证recv读取的是一个完整的报文，然后做数据处理

// 服务器recv处理思路: 有一个string buff， recv读取一个str, buff += str
// 分析buff是否是一个完整报文，是:处理这份报文，并删除 buff 中的这段报文; 不是: 继续recv读取str加到buff中, buff会变多，再吃分析buff是否是一个完整报文

// 【服务器注意三】将服务器变为守护进程

// recv ： 你怎么保证，你读到的inbuffer，是一个完整完善的请求呢？不能保证


static void Usage(const std::string& proc)
{
    std::cout<<"\nUsage: " << proc << " port\n" << std::endl;
}


static Response calculatorHelper(const Request &req)
{
    Response resp(0,0);
    switch (req._op)
    {
    case '+':
        resp._result = req._x + req._y;
        break;
    case '-':
        resp._result = req._x - req._y;
        break;
    case '*':
        resp._result = req._x * req._y;
        break;
    case '/':
        if (0 == req._y)
            resp._code = 1;
        else
            resp._result = req._x / req._y;
        break;
    case '%':
        if (0 == req._y)
            resp._code = 2;
        else
            resp._result = req._x % req._y;
        break;
    default:
        resp._code = 3;
        break;
    }
    return resp;
}


void calculator(int sock)
{
    std::string inbuffer;
     while (true)
    {
        // 1. 读取
        bool res = Recv(sock, &inbuffer); 
        if (!res) // 读取失败退出
            break;


        // 2. 协议解析，保证得到一个完整的报文
        std::string package = Decode(inbuffer);
        std::cout << "完整的报文" << package << std::endl;
        if (package.empty()) // 不是继续读取数据，凑成一个以上的完整报文
            continue;
        logMessage(NORMAL, "%s", package.c_str());

        // 3. 反序列化 （服务端处理报文数据）
        Request req;
        req.Deserialize(package); // 字节流 -> 结构化

        // 4. 业务逻辑(处理数据)
        Response resp = calculatorHelper(req);
        std::cout << resp._code << "  " << resp._result << std::endl;

        // 5. 序列化  （服务端将处理报文后的结果返回给客户端）
        std::string respString = resp.Serialize(); // 计算结果进行序列化 
        std::cout << "处理报文后的结果" << respString << std::endl;

        // 6. 添加长度信息，形成一个完整的报文  "length\r\ncode result\r\n"
        respString = Encode(respString);
        std::cout << "添加长度信息" << respString << std::endl;

        // 7. send这里我们暂时先这样写，多路转接的时候，再来谈发送的问题
        Send(sock, respString);
    }
}


// ./CalServer port

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    // signal(SIGPIPE, SIG_IGN);
    // 将服务器守护进程化
    MyDaemon();
    std::unique_ptr<TcpServer> server(new TcpServer(atoi(argv[1])));
    server->BindService(calculator);

    server->Start();
}